#include "Audio.h"

using namespace FMOD;

AudioSystem::AudioSystem() :
	pBGMChannel(nullptr)
{
	auto result = System_Create(&pSystem);
	pSystem->setSoftwareChannels(128);
	pSystem->setHardwareChannels(128);
	pSystem->init(128, FMOD_INIT_NORMAL, 0);
	pSystem->createSoundGroup("bgm", &pBGMGroup);
	pSystem->createSoundGroup("sfx", &pSFXGroup);
	pSystem->getSoftwareFormat(&sampleRate, 0, 0, 0, 0, 0);
	curbgm = { nullptr,nullptr };
}

AudioSystem::~AudioSystem()
{
	SAFE_RELEASE_FMOD_COMPONENT(pBGMGroup);
	SAFE_RELEASE_FMOD_COMPONENT(pSFXGroup);
	SAFE_RELEASE_FMOD_COMPONENT(pSystem);
}

int AudioSystem::loadSoundFromFile(const std::string & pFilename, SOUNDTYPE sndtype, bool isSoftware)
{
	const char* filepath = pFilename.c_str();
	int sndindex = 0;
	FMOD_MODE mode = (isSoftware ? FMOD_SOFTWARE : FMOD_HARDWARE) | FMOD_2D;
	FMOD_RESULT result;
	switch (sndtype)
	{
		case BGM:
			releaseBGMsnd();
			result = pSystem->createStream(filepath, mode | FMOD_LOOP_OFF, 0, &curbgm.pSnd);
			break;
		case SFX:
			Sound* sfx;
			result = pSystem->createSound(filepath, mode | FMOD_LOOP_OFF, 0, &sfx);
			//result = sfx->setSoundGroup(pSFXGroup);
			//result = pSFXGroup->getNumSounds(&sndindex);
			sfxPool.push_back(sfx);
			sndindex = sfxPool.size() - 1;
			break;
		case LOAD_BGM:
			releaseBGMsnd();
			result = pSystem->createSound(filepath, mode | FMOD_LOOP_OFF, 0, &curbgm.pSnd);
			break;
		case LOOP_BGM:
			releaseBGMsnd();
			result = pSystem->createSound(filepath, mode | FMOD_LOOP_NORMAL, 0, &curbgm.pSnd);
			break;
		default:
			break;
	}
	curbgm.pSnd->getLength(&bgmLength, FMOD_TIMEUNIT_MS);
	if (result != FMOD_OK)
		auto err = result;
	return sndindex;
}

void AudioSystem::playLoadedBGM(float volume)
{
	auto result = pSystem->playSound(
		FMOD_CHANNEL_FREE,
		curbgm.pSnd,
		true,
		&curbgm.pChl
	);
	curbgm.pChl->setVolume(volume);
	curbgm.pChl->setPriority(0);
	curbgm.pChl->setPaused(false);
}

void AudioSystem::playBGM(const std::string & pFilename, float volume)
{
	auto result = pSystem->createStream(pFilename.c_str(), FMOD_SOFTWARE | FMOD_LOOP_NORMAL, 0, &pCurBGM);
	result = pSystem->playSound(
		FMOD_CHANNEL_FREE,
		pCurBGM,
		true,
		&pBGMChannel
	);
	update();
	pBGMChannel->setVolume(volume);
	pBGMChannel->setPriority(0);
	pBGMChannel->setPaused(false);
}

AudioSystem::BGMObject AudioSystem::playBGMReturnCurrentObject(const std::string & pFilename, float volume)
{
	releaseBGMsnd();
	BGMObject obj;
	auto result = pSystem->createStream(pFilename.c_str(),
										FMOD_SOFTWARE | FMOD_LOOP_NORMAL | FMOD_CREATESTREAM,
										0,
										&obj.pSnd);
	result = pSystem->playSound(
		FMOD_CHANNEL_FREE,
		obj.pSnd,
		true,
		&obj.pChl
	);
	update();
	obj.pChl->setVolume(volume);
	obj.pChl->setPaused(false);
	return obj;
}

void AudioSystem::playBGMFadeIn(const std::string & pFilename, int fadetime, float volume, int startpos)
{
	update();
	curbgm.pChl->stop();
	//curbgm.pSnd->release();
	curbgm = playBGMReturnCurrentObject(pFilename, 0);
	setChannelPosition(curbgm.pChl, startpos);
	setChannelVolume(curbgm.pChl, fadetime, volume);
}

void AudioSystem::changeBGMFadeInOut(const std::string & pFilename, int fadetime, float volume, int startpos)
{
	update();
	thread changebgm([&]
	{
		bgmFading = true;
		auto tempobj = curbgm;
		playBGMFadeIn(pFilename, fadetime, volume, startpos);
		if (tempobj.pChl != nullptr)
		{
			tempobj.pChl->stop();
			//try
			//{
			//	auto result = tempobj.pSnd->release();
			//	if (result == FMOD_OK) tempobj.pSnd = nullptr;
			//}
			//catch (exception e) { return; };
		}
	});
	changebgm.detach();
}

void AudioSystem::pauseBGM()
{
	pBGMChannel->setPaused(true);
}

void AudioSystem::resumeBGM()
{
	pBGMChannel->setPaused(false);
}

void AudioSystem::setBGMPosition(unsigned int posi)
{
	pBGMChannel->setPosition(posi, FMOD_TIMEUNIT_MS);
}

void AudioSystem::setChannelPosition(Channel * channel, unsigned int posi)
{
	channel->setPosition(posi, FMOD_TIMEUNIT_MS);
}

void AudioSystem::setBGMVolume(int fadetime, float volume)
{
	if (fadetime == 0) pBGMChannel->setVolume(volume);
	else
	{
		thread tbgmvol([=]
		{
			bgmFading = true;
			int loopcount = fadetime / 20;
			float curvol;
			pBGMChannel->getVolume(&curvol);
			float volstep = (volume - curvol) / loopcount;
			bool ischlplaying;
			for (int i = 0; i < loopcount; i++)
			{
				pBGMChannel->isPlaying(&ischlplaying);
				if (!ischlplaying)
				{
					pBGMChannel->setVolume(volume);
					break;
				}
				curvol += volstep;
				pBGMChannel->setVolume(curvol);
				this_thread::sleep_for(chrono::milliseconds(20));
			}
			bgmFading = false;
		});
		tbgmvol.detach();
	}
}

void AudioSystem::setChannelVolume(Channel * channel, int fadetime, float volume)
{
	if (fadetime == 0) channel->setVolume(volume);
	else
	{
		thread tbgmvol([=]
		{
			int loopcount = fadetime / 20;
			float curvol;
			auto result = channel->getVolume(&curvol);
			float volstep = (volume - curvol) / loopcount;
			bool ischlplaying;
			for (int i = 0; i < loopcount; i++)
			{
				result = channel->isPlaying(&ischlplaying);
				curvol += volstep;
				if (result != FMOD_OK || !ischlplaying || curvol < 0 || curvol > 1)
				{
					channel->setVolume(volume);
					break;
				}
				channel->setVolume(curvol);
				this_thread::sleep_for(chrono::milliseconds(20));
			}
		});
		tbgmvol.detach();
	}
}

void AudioSystem::setMasterVolume(float volume)
{
	ChannelGroup* masterchlgrp;
	pSystem->getMasterChannelGroup(&masterchlgrp);
	masterchlgrp->setVolume(volume);
}


void AudioSystem::stopBGM(bool releaseSound)
{
	FMOD_RESULT result = curbgm.pChl->stop();
	if (releaseSound) releaseBGMsnd();
}

void AudioSystem::stopAllSound(bool releaseSound)
{
	FMOD_RESULT result;
	ChannelGroup* mstgrp;
	result = pSystem->getMasterChannelGroup(&mstgrp);
	result = mstgrp->stop();
}

void AudioSystem::releaseBGMsnd()
{
	if (curbgm.pSnd != nullptr)
	{
		curbgm.pSnd->release();
		curbgm.pSnd = nullptr;
	}
}

void AudioSystem::playSFX(int sfxIndex)
{
	//Sound* pSFX;
	//pSFXGroup->getSound(sfxIndex, &pSFX);
	//pSystem->playSound(
	//	FMOD_CHANNEL_FREE,
	//	pSFX,
	//	false,
	//	nullptr
	//);
	pSystem->playSound(FMOD_CHANNEL_FREE, sfxPool[sfxIndex], false, nullptr);
}

void AudioSystem::releaseAllSFX()
{
	//int numsfx;
	//pSFXGroup->getNumSounds(&numsfx);
	//for (int i = 0; i < numsfx; i++)
	//{
	//	Sound* sfx;
	//	pSFXGroup->getSound(i, &sfx);
	//	sfx->release();
	//}
	//pSFXGroup->release();
	for (auto sfx : sfxPool)
	{
		sfx->release();
	}
	sfxPool.clear();
}

unsigned int AudioSystem::getBgmLength()
{
	return bgmLength;
}

unsigned int AudioSystem::getBgmPosition()
{
	unsigned int posi = 0;
	//auto result = pBGMChannel->getPosition(&posi, FMOD_TIMEUNIT_MS | FMOD_TIMEUNIT_BUFFERED);
	FMOD_Channel_GetPosition((FMOD_CHANNEL*)curbgm.pChl, &posi, FMOD_TIMEUNIT_MS);
	return posi;
}

AudioSystem::WaveData AudioSystem::getWaveData()
{
	float tempData[1];
	WaveData data;
	pSystem->getWaveData(tempData, 1, 0);
	data.L = tempData[0];
	data.MIX = abs(tempData[0]);
	pSystem->getWaveData(tempData, 1, 1);
	data.R = tempData[0];
	data.MIX += abs(tempData[0]);
	data.MIX /= 2;
	return data;
}

int AudioSystem::getDSPClockInMS()
{
	unsigned int hi, lo;
	pSystem->getDSPClock(&hi, &lo);
	unsigned long clock = (((unsigned long)hi & (unsigned long)0x0000FFFF) << 16) + lo;
	return 1000 * clock / sampleRate;
}

int AudioSystem::getSfxPoolSize()
{
	return (int)sfxPool.size();
}

void AudioSystem::update()
{
	pSystem->update();
}
